/****************************************************************************
 * Copyright (c) 2013 Markus Alexander Kuppe and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * Contributors:
 *   Markus Alexander Kuppe - initial API and implementation
 *
 * SPDX-License-Identifier: EPL-2.0
 *****************************************************************************/
package org.eclipse.ecf.internal.discovery;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ecf.core.identity.Namespace;
import org.eclipse.ecf.discovery.*;
import org.eclipse.ecf.discovery.identity.*;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

/**
 * An IServiceInfoServiceListener is a whiteboard pattern listener responsible
 * to handle IServiceInfos registered in the OSGi service registry.
 */
public class IServiceInfoServiceListener {

	private final ServiceTracker serviceTracker;

	void logException(String message, Throwable t) {
		DiscoveryPlugin.getDefault().log(new Status(IStatus.ERROR, DiscoveryPlugin.PLUGIN_ID, message, t));
	}

	public IServiceInfoServiceListener(final IDiscoveryAdvertiser advertiser) {
		final BundleContext bundleContext = DiscoveryPlugin.getDefault().getBundleContext();
		serviceTracker = new ServiceTracker(bundleContext, IServiceInfo.class, new ServiceTrackerCustomizer() {

			public Object addingService(ServiceReference reference) {
				final IServiceInfo serviceInfo = (IServiceInfo) bundleContext.getService(reference);
				try {
					advertiser.registerService(convertToProviderSpecific(advertiser, serviceInfo));
				} catch (Exception e) {
					logException("Advertiser.registerService failed", e);
				}
				return serviceInfo;
			}

			public void modifiedService(ServiceReference reference, Object service) {
				// TODO discovery containers might require to
				// unregisterService first
				try {
					advertiser.registerService(convertToProviderSpecific(advertiser, (IServiceInfo) service));
				} catch (Exception e) {
					logException("Advertiser.modifiedService failed", e);
				}
			}

			public void removedService(ServiceReference reference, Object service) {
				try {
					advertiser.unregisterService(convertToProviderSpecific(advertiser, (IServiceInfo) service));
				} catch (Exception e) {
					logException("Advertiser.removedService failed", e);
				}
			}
		});
		serviceTracker.open();
	}

	/**
	 * Converts the generic (not discovery provider specific WRT
	 * IServiceID/IServiceTypeID) IServiceInfo into a discovery provider
	 * specific one. This is required so that discovery providers can correctly
	 * advertise services.
	 */
	private IServiceInfo convertToProviderSpecific(final IDiscoveryAdvertiser advertiser,
			final IServiceInfo genericInfo) {

		// Convert similar to
		// org.eclipse.ecf.provider.discovery.CompositeDiscoveryContainer.getServiceIDForDiscoveryContainer(IServiceID,
		// IDiscoveryLocator)
		final Namespace servicesNamespace = advertiser.getServicesNamespace();

		final IServiceID genericServiceID = genericInfo.getServiceID();
		final ServiceID specificServiceID = (ServiceID) servicesNamespace.createInstance(
				new Object[] { genericServiceID.getServiceTypeID().getName(), genericServiceID.getLocation() });

		final IServiceTypeID serviceTypeID = specificServiceID.getServiceTypeID();

		return new ServiceInfo(genericServiceID.getLocation(), genericInfo.getServiceName(), serviceTypeID,
				genericInfo.getPriority(), genericInfo.getWeight(), genericInfo.getServiceProperties(),
				genericInfo.getTTL());
	}

	public void dispose() {
		serviceTracker.close();
	}
}
